home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / Libraries / GUSI / GUSIFSp.cp < prev    next >
Text File  |  1993-11-16  |  16KB  |  785 lines

  1. /*********************************************************************
  2. Project    :    GUSI                -    Grand Unified Socket Interface
  3. File        :    GUSIFSp.cp        -    Dealing with paths
  4. Author    :    Matthias Neeracher <neeri@iis.ethz.ch>
  5. Started    :    15Jun92                                Language    :    MPW C++
  6.                 06Sep92    MN    Clear ioACUser
  7.                 12Sep92    MN    Renamed Paths.h to GUSIFSp_P.h
  8.                 15Nov92    MN    Forgot a few consts
  9.                 15Nov92    MN    Rename GUSIFSp_P.h to TFileSpec.h (there we go again)
  10.                 15Jan93    MN    IsParentOf
  11.                 06Feb93    MN    Use FSMakeFSSpec if possible
  12.                 01Mar93    MN    Bless
  13.                 21Jun93    MN    Throw out the inline
  14.                 17Jul93    MN    LastInfo
  15.                 27Sep93    MN    FSpSmartMove
  16.                 24Oct93    MN    Allow changing case in a rename
  17. Last        :    24Oct93
  18. *********************************************************************/
  19.  
  20. #include "GUSI_P.h"
  21. #include "TFileSpec.h"
  22.  
  23. #include <Errors.h>
  24. #include <Memory.h>
  25. #include <Aliases.h>
  26. #include <string.h>
  27. #include <ioctl.h>
  28. #include <fcntl.h>
  29. #include <errno.h>
  30. #include <TextUtils.h>
  31.  
  32. OSErr         TFileSpec::error;
  33. short         TFileSpec::curVol;
  34. long            TFileSpec::curDir    =    -1;
  35. CInfoPBRec    TFileSpec::lastInfo;
  36.  
  37. OSErr TFileSpec::ChDir(const TFileSpec & spec)
  38. {
  39.     TFileSpec nudir(spec);
  40.     
  41.     nudir += "\p";
  42.     
  43.     if (error)
  44.         return error;
  45.     
  46.     curVol    =    nudir.vRefNum;
  47.     curDir    =    nudir.parID;
  48.  
  49.     return noErr;
  50. }
  51.  
  52. static OSErr CurrentDir(short & vRefNum, long & parID)
  53. {
  54.     OSErr        error;
  55.     WDPBRec    vol;
  56.     Str63        name;
  57.     
  58.     vol.ioNamePtr    =    name;
  59.     
  60.     if (error = PBHGetVolSync(&vol))
  61.         return error;
  62.         
  63.     vRefNum    =    vol.ioWDVRefNum;
  64.     parID        =    vol.ioWDDirID;
  65.     
  66.     return noErr;
  67. }
  68.  
  69. OSErr TFileSpec::DefaultDir()
  70. {
  71.     if (curDir != -1)    {
  72.         vRefNum    =    curVol;
  73.         parID        =    curDir;
  74.         
  75.         return noErr;
  76.     } else
  77.         return error = CurrentDir(vRefNum, parID);
  78. }
  79.  
  80. OSErr TFileSpec::Default()
  81. {
  82.     if (!DefaultDir())
  83.         --*this;
  84.     
  85.     return error;
  86. }
  87.  
  88. TFileSpec::TFileSpec(const FSSpec & spec, Boolean useAlias)                            
  89.     : FSSpec(spec) 
  90. {
  91.     if (!useAlias && hasAlias)
  92.         Resolve();
  93. }
  94.  
  95. TFileSpec::TFileSpec(short vRefNum, long parID, ConstStr31Param name, Boolean useAlias)
  96. {
  97.     if (!hasMakeFSSpec || FSMakeFSSpec(vRefNum, parID, name, this)) {
  98.         this->vRefNum    =    vRefNum;
  99.         this->parID        =    parID;
  100.         memcpy(this->name, name, *name+1);
  101.     }
  102.     
  103.     if (!useAlias && hasAlias)
  104.         Resolve();    
  105. }
  106.  
  107. TFileSpec::TFileSpec(short wd, ConstStr31Param name, Boolean useAlias)
  108. {
  109.     if (!hasMakeFSSpec || FSMakeFSSpec(wd, 0, name, this)) {
  110.         WDPBRec     wdPB;
  111.         
  112.         wdPB.ioNamePtr     = nil;
  113.         wdPB.ioVRefNum     = wd;
  114.         wdPB.ioWDIndex     = 0;
  115.         wdPB.ioWDProcID     = 0;
  116.         
  117.         /* Change the Working Directory number in vRefnum into a real vRefnum */
  118.         /* and DirID. The real vRefnum is returned in ioVRefnum, and the real */
  119.         /* DirID is returned in ioWDDirID. */
  120.         
  121.         if (error = PBGetWDInfoSync(&wdPB))
  122.             return;
  123.         
  124.         vRefNum    = wdPB.ioWDVRefNum;
  125.         parID        = wdPB.ioWDDirID;
  126.         memcpy(this->name, name, *name+1);
  127.     }
  128.     
  129.     if (!useAlias && hasAlias)
  130.         Resolve();
  131. }
  132.  
  133. TFileSpec::TFileSpec(OSType object, short vol, long dir)
  134. {
  135.     if (object == kTempFileType && dir) {
  136.         vRefNum    =    vol;
  137.         parID        =    dir;
  138.     } else if (
  139.         error = 
  140.             FindFolder(
  141.                 vol, (object == kTempFileType) ? kTemporaryFolderType : object,
  142.                 true, &vRefNum, &parID)
  143.     ) 
  144.         return;
  145.         
  146.     if (object == kTempFileType) {
  147.         static long nr = -1;
  148.         
  149.         strcpy((char *) name, (char *) "\ptmp00000");
  150.         
  151.         do {
  152.             nr = (nr + 1) % 100000;
  153.             
  154.             sprintf((char *) name+4, "%05ld", nr);
  155.         } while (Exists());
  156.     } else {
  157.         *this -= 1;
  158.     }
  159. }
  160.  
  161. void TFileSpec::Bless()
  162. {
  163.     if (hasMakeFSSpec) {
  164.         FSSpec    spec    =    *this;    // Dont know what happens to an aliased name
  165.     
  166.         error = FSMakeFSSpec(spec.vRefNum, spec.parID, spec.name, this);
  167.     } else
  168.         error = noErr;
  169. }
  170.  
  171. #define    maxPathLen 512
  172.  
  173. static char fullPath[maxPathLen];
  174.  
  175. /* Convert an FSSpec into a full pathname. The pathname is accumulated on the
  176.    high end of path to avoid excessive copying.
  177. */
  178.  
  179. char * TFileSpec::FullPath() const
  180. {
  181.     char *            curPath;
  182.     
  183.     /* Special case: a volume was specified */
  184.     if (parID == fsRtParID)    {
  185.         memcpy(fullPath, name+1, *name);
  186.         curPath        =    fullPath+*name;
  187.         curPath[0]    =    ':';
  188.         curPath[1]    =    0;
  189.         error            =    noErr;
  190.         
  191.         return fullPath;
  192.     }
  193.         
  194.     fullPath[maxPathLen-1]    =    0;
  195.     curPath                         =    fullPath+maxPathLen-*name-1;
  196.     
  197.     memcpy(curPath, name+1, *name);
  198.     
  199.    lastInfo.dirInfo.ioNamePtr = (StringPtr) fullPath;
  200.    lastInfo.dirInfo.ioDrParID = parID;
  201.  
  202.    do {
  203.       lastInfo.dirInfo.ioVRefNum     = vRefNum;
  204.       lastInfo.dirInfo.ioFDirIndex     = -1;
  205.       lastInfo.dirInfo.ioDrDirID     = lastInfo.dirInfo.ioDrParID;
  206.  
  207.       if (error = PBGetCatInfoSync(&lastInfo))
  208.              return "";
  209.       *--curPath    =    ':';
  210.         curPath    -=    *fullPath;
  211.         memmove(curPath, fullPath+1, *fullPath);
  212.    } while (lastInfo.dirInfo.ioDrDirID != fsRtDirID);
  213.  
  214.     return curPath;
  215. }
  216.  
  217. char * TFileSpec::RelPath() const
  218. {
  219.     short            curVRef;
  220.     long            curDirID;
  221.     char *        curPath;
  222.     
  223.     if (CurrentDir(curVRef, curDirID))
  224.         return FullPath();
  225.         
  226.     /* Special case: a volume was specified */
  227.     if (parID == fsRtParID)    {
  228.         if (vRefNum == curVRef && curDirID == fsRtDirID)
  229.             curPath = fullPath;
  230.         else {
  231.             memcpy(fullPath, name+1, *name);
  232.             curPath        =    fullPath+*name;
  233.         }
  234.         
  235.         curPath[0]    =    ':';
  236.         curPath[1]    =    0;
  237.         
  238.         return fullPath;
  239.     }
  240.  
  241.     lastInfo.dirInfo.ioNamePtr        =    (StringPtr)fullPath;
  242.     lastInfo.dirInfo.ioVRefNum     =     curVRef;
  243.     lastInfo.dirInfo.ioFDirIndex     =     -1;
  244.     lastInfo.dirInfo.ioDrDirID     =     curDirID;
  245.     
  246.     if (error = PBGetCatInfoSync(&lastInfo))
  247.         return "";
  248.     
  249.     if (curVRef == vRefNum 
  250.         && lastInfo.dirInfo.ioDrParID == parID
  251.         && !memcmp (fullPath, name, *fullPath+1)
  252.     )    {
  253.         fullPath[0]    =    ':';
  254.         fullPath[1] =     0;
  255.         
  256.         return fullPath;
  257.     }
  258.         
  259.     fullPath[maxPathLen-1]    =    0;
  260.     curPath                         =    fullPath+maxPathLen-*name-1;
  261.     
  262.     memcpy(curPath, name+1, *name);
  263.     
  264.    lastInfo.dirInfo.ioNamePtr = (StringPtr) fullPath;
  265.    lastInfo.dirInfo.ioDrParID = parID;
  266.  
  267.    do {
  268.       *--curPath    =    ':';
  269.          
  270.         /* Test fur current directory */
  271.         if (curVRef == vRefNum && curDirID == lastInfo.dirInfo.ioDrParID)
  272.             return strchr(curPath+1, ':') ? curPath : curPath+1;
  273.             
  274.         lastInfo.dirInfo.ioVRefNum     = vRefNum;
  275.         lastInfo.dirInfo.ioFDirIndex = -1;
  276.         lastInfo.dirInfo.ioDrDirID     = lastInfo.dirInfo.ioDrParID;
  277.         
  278.         if (error = PBGetCatInfoSync(&lastInfo))
  279.             return "";
  280.         curPath    -=    *fullPath;
  281.         memmove(curPath, fullPath+1, *fullPath);
  282.    } while (lastInfo.dirInfo.ioDrDirID != fsRtDirID);
  283.  
  284.     return curPath;
  285. }    
  286.  
  287. OSErr    TFileSpec::CatInfo(CInfoPBRec & info, Boolean dirInfo) const
  288. {
  289.    info.dirInfo.ioVRefNum         = vRefNum;
  290.    info.dirInfo.ioDrDirID         = parID;
  291.    info.dirInfo.ioNamePtr         = name;
  292.    info.dirInfo.ioFDirIndex     = dirInfo ? -1 : 0;
  293. #ifdef OnceThisFieldIsDefined
  294.     info.dirInfo.ioACUser         = 0;
  295. #else
  296.     info.dirInfo.filler2         = 0;
  297. #endif
  298.         
  299.    return error = PBGetCatInfoSync(&info);
  300. }
  301.  
  302. TFileSpec TFileSpec::operator--()
  303. {
  304.       CatInfo(lastInfo, true);
  305.     
  306.     if (!error)
  307.         parID    = lastInfo.dirInfo.ioDrParID;
  308.     
  309.     return *this;
  310. }
  311.  
  312. TFileSpec TFileSpec::operator-=(int levels)
  313. {
  314.     while (levels-- > 0)    {
  315.         --*this;
  316.         if (this->Error())
  317.             break;
  318.     }
  319.     
  320.     return *this;
  321. }
  322.  
  323. TFileSpec TFileSpec::operator-(int levels) const
  324. {
  325.     TFileSpec    spec    =    *this;
  326.     
  327.     return spec -= levels;
  328. }
  329.  
  330. OSErr TFileSpec::Resolve(const CInfoPBRec & info)
  331. {
  332.     Boolean        isFolder;
  333.     Boolean        wasAlias;
  334.     
  335.     return error = 
  336.         (hasAlias && IsAlias(info)) ? 
  337.             ResolveAliasFile(this, true, &isFolder, &wasAlias) :
  338.             noErr;
  339. }
  340.  
  341. OSErr TFileSpec::Resolve(Boolean gently)
  342. {
  343.     CatInfo(lastInfo);
  344.     
  345.     if (error)
  346.         if (gently)
  347.             return error = noErr;
  348.         else
  349.             return error;
  350.     else
  351.         return Resolve(lastInfo);
  352. }
  353.  
  354. Boolean TFileSpec::Exists() const
  355. {
  356.     Boolean        res;
  357.     
  358.     res     = !CatInfo(lastInfo);
  359.     error    =    noErr;
  360.     
  361.     return res;
  362. }
  363.  
  364. Boolean TFileSpec::operator==(const TFileSpec & other) const
  365. {
  366.     return     vRefNum == other.vRefNum 
  367.         &&        parID == other.parID 
  368.         &&     EqualString(name, other.name, false, true);
  369. }
  370.  
  371. Boolean TFileSpec::operator!=(const TFileSpec & other) const
  372. {
  373.     return     vRefNum != other.vRefNum 
  374.         ||        parID != other.parID 
  375.         ||     !EqualString(name, other.name, false, true);
  376. }
  377.  
  378. Boolean TFileSpec::IsParentOf(const TFileSpec & other) const
  379. {
  380.     for (TFileSpec oth = other - 1; !oth.Error() && *this != oth; --oth);
  381.     
  382.     return !oth.Error();
  383. }
  384.  
  385. TFileSpec TFileSpec::operator+=(ConstStr31Param name)
  386. {
  387.     if (*name > 63)
  388.         return bdNamErr;
  389.         
  390.    if (CatInfo(lastInfo))
  391.         goto punt;
  392.     
  393.     // Resolve if an alias
  394.     
  395.     if (IsAlias(lastInfo))
  396.         if (Resolve(lastInfo) || CatInfo(lastInfo))
  397.             goto punt;
  398.     
  399.     if (IsFile(lastInfo)) {
  400.         error = bdNamErr;
  401.         
  402.         goto punt;
  403.     }
  404.     
  405.     parID    = lastInfo.dirInfo.ioDrDirID;
  406.  
  407.     memcpy(this->name, name, *name+1);
  408.  
  409. punt:    
  410.     return *this;
  411. }
  412.  
  413. TFileSpec TFileSpec::operator+(ConstStr31Param name) const
  414. {
  415.     TFileSpec    spec    =    *this;
  416.     
  417.     return spec += name;
  418. }
  419.  
  420. TFileSpec TFileSpec::operator+=(const char * name)
  421. {
  422.     int    len = strlen(name);
  423.     
  424.     if (len > 63)
  425.         return bdNamErr;
  426.         
  427.    if (CatInfo(lastInfo))
  428.         goto punt;
  429.     
  430.     // Resolve if an alias
  431.     
  432.     if (IsAlias(lastInfo))
  433.         if (Resolve(lastInfo) || CatInfo(lastInfo))
  434.             goto punt;
  435.     
  436.     if (IsFile(lastInfo)) {
  437.         error = bdNamErr;
  438.         
  439.         goto punt;
  440.     }
  441.     
  442.     parID    = lastInfo.dirInfo.ioDrDirID;
  443.  
  444.     memcpy(this->name+1, name, *this->name = len);
  445.  
  446. punt:    
  447.     return *this;
  448. }
  449.  
  450. TFileSpec TFileSpec::operator+(const char * name) const
  451. {
  452.     TFileSpec    spec    =    *this;
  453.     
  454.     return spec += name;
  455. }
  456.  
  457. TFileSpec TFileSpec::operator[](short index) const
  458. {
  459.     TFileSpec    spec     = *this;
  460.     
  461.    lastInfo.dirInfo.ioVRefNum     = spec.vRefNum;
  462.    lastInfo.dirInfo.ioDrDirID         = spec.parID;
  463.    lastInfo.dirInfo.ioNamePtr     = spec.name;
  464.    lastInfo.dirInfo.ioFDirIndex     = index;
  465.  
  466.    error = PBGetCatInfoSync(&lastInfo);
  467.     
  468.     return spec;
  469. }
  470.  
  471. TFileSpec::TFileSpec(const char * path, Boolean useAlias)
  472. {
  473.     int            pathLen    =     strlen(path);
  474.     StringPtr    name        =    (StringPtr) fullPath;
  475.     char *        nextPath;
  476.     
  477.     if (hasMakeFSSpec) {
  478.         DefaultDir();
  479.         
  480.         nextPath = (char *) memccpy((char *) name + 1, path, 0, 254);
  481.     
  482.         if (nextPath)
  483.             name[0] = nextPath - (char *) name - 2;
  484.         else
  485.             name[0] = 254;
  486.         
  487.         switch (error = FSMakeFSSpec(vRefNum, parID, name, this)) {
  488.         case fnfErr:
  489.             error = noErr;
  490.             // Fall through:
  491.         case noErr:
  492.             goto done;                    // Going down slow ain't the only way to go
  493.         default:
  494.             break;
  495.         }
  496.     }
  497.     
  498.     if (path[0] == ':' || !(nextPath = strchr(path, ':'))) {
  499.         Default();
  500.         
  501.         if (*path == ':')
  502.             ++path;
  503.     } else {
  504.         ParamBlockRec    vol;
  505.         
  506.         if (nextPath - (char *) path > 62) {
  507.             error = bdNamErr;
  508.             
  509.             return;
  510.         }
  511.             
  512.         memcpy(name+1, (char *) path, *name = nextPath - (char *)  path + 1);
  513.         
  514.         vol.volumeParam.ioNamePtr    =    name;
  515.         vol.volumeParam.ioVolIndex    =    -1;
  516.         
  517.         if (error = PBGetVInfoSync(&vol))
  518.             return;
  519.         
  520.         vRefNum    =    vol.volumeParam.ioVRefNum;
  521.         parID        =    fsRtDirID;
  522.         
  523.         path = nextPath + 1;
  524.  
  525.         --*this;
  526.     }
  527.         
  528.     if (error)
  529.         return;
  530.     
  531.     while (*path) {
  532.         if (*path == ':')    {
  533.             --*this;
  534.             ++path;
  535.             
  536.             if (error)
  537.                 return;
  538.             else
  539.                 continue;
  540.         }
  541.         
  542.         if (nextPath = strchr(path, ':'))
  543.             *nextPath = 0;
  544.         
  545.         *this += path;
  546.  
  547.         if (error)
  548.             return;
  549.             
  550.         if (nextPath) {
  551.             *nextPath = ':';
  552.             path = nextPath + 1;
  553.         } else
  554.             break;
  555.     }
  556.  
  557. done:
  558.     if (!useAlias && hasAlias)
  559.         Resolve();
  560. }
  561.  
  562. /* Convert a FSSpec into a full pathname. */
  563. char * FSp2FullPath(const FSSpec * desc)
  564. {
  565.     TFileSpec    spec(*desc);
  566.     
  567.     return spec.FullPath();
  568. }
  569.  
  570. /* Convert a FSSpec into a relative pathname. */
  571. char * FSp2RelPath(const FSSpec * desc)
  572. {
  573.     TFileSpec    spec(*desc);
  574.     
  575.     return spec.RelPath();
  576. }
  577.  
  578. /* Convert a working directory & file name into a FSSpec. */
  579. OSErr WD2FSSpec(short wd, ConstStr31Param name, FSSpec * desc)
  580. {
  581.     TFileSpec    spec(wd, name);
  582.     
  583.     *desc = spec;
  584.     
  585.     return spec.Error();
  586. }
  587.  
  588. /* Convert a pathname into a file spec. */
  589. OSErr Path2FSSpec(const char * path, FSSpec * desc)
  590. {
  591.     TFileSpec    spec(path);
  592.     
  593.     *desc = spec;
  594.     
  595.     return spec.Error();
  596. }
  597.  
  598. /* Convert a working directory & file name into a FSSpec. */
  599. OSErr Special2FSSpec(OSType object, short vol, long dirID, FSSpec * desc)
  600. {
  601.     TFileSpec    spec(object, vol, dirID);
  602.     
  603.     *desc = spec;
  604.     
  605.     return spec.Error();
  606. }
  607.  
  608. /* Return FSSpec of (vRefNum, parID) */
  609. OSErr FSpUp(FSSpec * desc)
  610. {
  611.     TFileSpec    spec(*desc);
  612.  
  613.     *desc = --spec;
  614.     
  615.     return spec.Error();
  616. }
  617.  
  618. /* Return FSSpec of file in directory denoted by desc */
  619. OSErr FSpDown(FSSpec * desc, ConstStr31Param name)
  620. {
  621.     TFileSpec    spec(*desc);
  622.  
  623.     *desc    = spec + name;
  624.     
  625.     return spec.Error();
  626. }
  627.  
  628. /* Call GetCatInfo for file system object. */
  629. OSErr    FSpCatInfo(const FSSpec * desc, CInfoPBRec * info)
  630. {
  631.     TFileSpec    spec(*desc);
  632.     
  633.     return spec.CatInfo(*info);
  634. }
  635.  
  636. /* Return FSSpec of nth file in directory denoted by (vRefNum, parID) */
  637. OSErr FSpIndex(FSSpec * desc, short n)
  638. {
  639.     TFileSpec    spec(*desc);
  640.     
  641.     *desc = spec[n];
  642.     
  643.     return spec.Error();
  644. }
  645.  
  646. static OSErr DefaultVRef(short & vRef)
  647. {
  648.     OSErr                err;
  649.     ParamBlockRec    vol;
  650.     
  651.     vol.volumeParam.ioNamePtr = nil;
  652.     
  653.     if (err = PBGetVolSync(&vol))
  654.         return err;
  655.     
  656.     vRef = vol.volumeParam.ioVRefNum;
  657.     
  658.     return noErr;
  659. }
  660.  
  661. OSErr FSpSmartMove(const FSSpec * from, const FSSpec * to)
  662. {
  663.     OSErr            err;
  664.     TFileSpec    fromspec(*from);
  665.     TFileSpec    tospec(*to);
  666.     TFileSpec    toparent = tospec - 1;
  667.     TFileSpec    corner;
  668.     CInfoPBRec    info;
  669.     
  670.     
  671.     if (!fromspec.vRefNum)
  672.         if (err = DefaultVRef(fromspec.vRefNum))
  673.             return err;
  674.             
  675.     if (!tospec.vRefNum)
  676.         if (err = DefaultVRef(tospec.vRefNum))
  677.             return err;
  678.             
  679.     if (fromspec.vRefNum != tospec.vRefNum)
  680.         return badMovErr;
  681.         
  682.     Boolean        diffname    =    !EqualString(fromspec.name, tospec.name, false, true);
  683.     Boolean        diffdir    =    fromspec.parID != tospec.parID;
  684.     Boolean        toexists = !tospec.CatInfo(info);
  685.     TFileSpec    tmpto;
  686.     
  687.     if (!diffname && !diffdir)    { /* Files are identical, except possibly for case */
  688.         err = noErr;
  689.         
  690.         goto cleanupcase;
  691.     }
  692.     
  693.     if (err = fromspec.CatInfo(info))
  694.         return err;
  695.     
  696.     if (!IsFile(info) && fromspec.IsParentOf(tospec))
  697.         return badMovErr;
  698.     
  699.     if (toexists)
  700.         if (!IsFile(info) && info.dirInfo.ioDrNmFls)
  701.             return fBsyErr;
  702.         else {
  703.             tmpto = TFileSpec(kTempFileType, tospec.vRefNum, tospec.parID);
  704.             
  705.             if (err = HRename(tospec.vRefNum, tospec.parID, tospec.name, tmpto.name))
  706.                 return err;
  707.         }
  708.             
  709.     if (!diffdir) {
  710.         err = HRename(fromspec.vRefNum, fromspec.parID, fromspec.name, tospec.name);
  711.         
  712.         goto cleanuptmp;
  713.     } else if (!diffname) {
  714.         err = 
  715.             CatMove(
  716.                 fromspec.vRefNum, fromspec.parID, fromspec.name, 
  717.                 toparent.parID, toparent.name);
  718.         
  719.         goto cleanuptmp;
  720.     }
  721.     
  722.     corner = TFileSpec(fromspec.vRefNum, fromspec.parID, tospec.name);
  723.     
  724.     if (!corner.Exists()) {
  725.         err = HRename(fromspec.vRefNum, fromspec.parID, fromspec.name, tospec.name);
  726.         
  727.         if (!err)
  728.             if (err = 
  729.                 CatMove(
  730.                     fromspec.vRefNum, fromspec.parID, tospec.name,
  731.                     toparent.parID, toparent.name)
  732.             )
  733.                 HRename(fromspec.vRefNum, fromspec.parID, tospec.name, fromspec.name);
  734.         
  735.         goto cleanuptmp;
  736.     }
  737.         
  738.     {
  739.         TFileSpec    secondcorner(kTempFileType, tospec.vRefNum, tospec.parID);
  740.         
  741.         memcpy(corner.name, secondcorner.name, secondcorner.name[0]+1);
  742.         
  743.         while (corner.Exists() || secondcorner.Exists()) {
  744.             TFileSpec newcorner(kTempFileType, secondcorner.vRefNum, secondcorner.parID);
  745.             
  746.             memcpy(corner.name, newcorner.name, newcorner.name[0]+1);
  747.             memcpy(secondcorner.name, newcorner.name, newcorner.name[0]+1);
  748.         }
  749.     }
  750.     
  751.     err = HRename(fromspec.vRefNum, fromspec.parID, fromspec.name, corner.name);
  752.     
  753.     if (!err)
  754.         if (err = 
  755.             CatMove(
  756.                 fromspec.vRefNum, fromspec.parID, corner.name,
  757.                 toparent.parID, toparent.name)
  758.         ) 
  759.             HRename(fromspec.vRefNum, fromspec.parID, corner.name, fromspec.name);
  760.         else if (err =
  761.             HRename(tospec.vRefNum, tospec.parID, corner.name, tospec.name)
  762.         ) {
  763.             TFileSpec fromparent = fromspec - 1;
  764.             
  765.             CatMove(
  766.                 tospec.vRefNum, tospec.parID, corner.name,
  767.                 fromparent.parID, fromparent.name);
  768.             HRename(fromspec.vRefNum, fromspec.parID, corner.name, fromspec.name);            
  769.         }
  770.     
  771. cleanuptmp:
  772.     if (toexists)
  773.         if (err)
  774.             HRename(tmpto.vRefNum, tmpto.parID, tmpto.name, tospec.name);
  775.         else
  776.             HDelete(tmpto.vRefNum, tmpto.parID, tmpto.name);
  777.  
  778. cleanupcase:
  779.     if (!err && !diffname)
  780.         return HRename(tospec.vRefNum, tospec.parID, tospec.name, tospec.name);
  781.     else
  782.         return err;
  783. }
  784.  
  785.